/*   Copyright (C) 2008  Jeroen v. Schagen & Youri Lima
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;

using Engine.Core;
using Engine.Math;

namespace Engine.Physics
{
    /// <summary>
    ///     Represents a physical body in a three-dimension space.
    ///     This body is a collection of masses, taken to be one.
    /// </summary>
    public class Body : Component
    {
        protected float mass;
        protected float density;

        protected Vector3 velocity;
        protected Vector3 acceleration;

        # region Constructor

        /// <summary>
        ///     Construct body, using a certain identifier. 
        /// </summary>
        /// 
        /// <param name="name"></param>
        public Body(String name) : base(name)
        {
            mass = 1.0f;
            density = 1.0f;

            velocity = Vector3.ZERO;
            acceleration = Vector3.ZERO;
        }

        # endregion


        # region Function

        /// <summary>
        ///     Newton's second law:
        /// 
        ///     The product of the mass and velocity is the momentum of 
        ///     the object, also called the "quantity of motion".
        /// 
        ///     Formula:
        ///     F = M * a
        /// </summary>
        public Vector3 Force
        {
            get
            {
                return mass * acceleration;
            }
        }

        /// <summary>
        ///     Isaac Newton's theory of universal gravitation is a physical 
        ///     law describing the gravitational attraction between bodies 
        ///     with mass.
        /// 
        ///     Every point mass attracts every other point mass by a force 
        ///     pointing along the line intersecting both points. The force 
        ///     is proportional to the product of the two masses and inversely 
        ///     proportional to the square of the distance between the point masses.
        /// 
        ///     Formula:
        ///     F = G * ((m1 * m2) / r)
        ///     G = 6.67e-11 N m2 kg-2
        /// </summary>
        /// 
        /// <param name="body"></param>
        /// <returns></returns>
        public Vector3 GravitationalForce(Body body)
        {
            // Store distance
            float r = position.Distance(body.Position);

            // Return force
            return (body.Position - this.position).Unit * (6.67e-11f * mass * body.Mass / (r * r));
        }

        /// <summary>
        ///     Represents the acceleration generated from gravitational force.
        /// 
        ///     Formula:
        ///     a1 = G * (m2 / r)
        ///     G = 6.67e-11 N m2 kg-2
        /// </summary>
        /// 
        /// <param name="body"></param>
        /// <returns></returns>
        public Vector3 GravitationalAcceleration(Body body)
        {
            // Store distance
            float r = position.Distance(body.Position);

            // Return force
            return (body.Position - this.position).Unit * (6.67e-11f * body.Mass / (r * r));
        }

        /// <summary>
        ///     The kinetic energy of an object is the extra energy which 
        ///     it possesses due to its motion. It is defined as the work 
        ///     needed to accelerate a body of a given mass from rest to 
        ///     its current velocity.
        /// 
        ///     Formula:
        ///     KE = 1/2 * M * V2
        /// </summary>
        public Vector3 KineticEnergy
        {
            get
            {
                return 0.5f * mass * ( velocity * velocity );
            }
        }

        /// <summary>
        ///     Calculate the acceleration that occurs on a body,
        ///     after being exposed to a certain force.
        /// 
        ///     The formula is derived from Newton's second law.
        /// 
        ///     Formula:
        ///     a = F / M
        /// </summary>
        /// 
        /// <param name="force">Force</param>
        /// <returns>Acceleration</returns>
        public Vector3 Motion(Vector3 force)
        {
            return force / mass;
        }

        /// <summary>
        ///     Simulate the body over a certain period of time.
        /// </summary>
        /// 
        /// <param name="time">Time</param>
        public override void Process(IProcessor handler, TimeSpan time)
        {
            // Apply acceleration in velocity
            Velocity = velocity + ( acceleration * (float) time.TotalSeconds );

            // Move body based on velocity
            Position = position + velocity;
        }

        # endregion


        # region Accessor

        /// <summary>
        ///     Mass is a fundamental concept, roughly corresponding to 
        ///     the intuitive idea of "how much matter there is in an object".
        /// 
        ///     In the SI system of units, mass is measured in 
        ///     kilograms: kg.
        /// </summary>
        public float Mass
        {
            get { return mass; }

            set
            {
                mass = value;

                // Notify observers
                NotifyObservers();
            }
        }

        /// <summary>
        ///     Density is defined as mass per unit volume. The ratio of 
        ///     the amount of matter in an object compared to its volume.
        /// 
        ///     In the SI system of units, density is measured in
        ///     kilograms per cubic meter: kg/m3
        /// </summary>
        public float Density
        {
            get { return density; }
            
            set
            {
                density = value;

                // Notify observers
                NotifyObservers();
            }
        }

        /// <summary>
        ///     Velocity is defined as the rate of change of position. 
        ///     It is a vector physical quantity; both speed and direction 
        ///     are required to define it. 
        /// 
        ///     In the SI (metric) system, velocity is measured in 
        ///     metres per second: m/s or m*s-1.
        /// </summary>
        public Vector3 Velocity
        {
            get { return velocity; }
            
            set
            {
                velocity = value;

                // Notify observers
                NotifyObservers();
            }
        }

        /// <summary>
        ///     Acceleration is defined as the rate of change of velocity.
        /// 
        ///     In the SI (metric) system, acceleration is measured in 
        ///     meters per second squared: m*s-2
        /// </summary>
        public Vector3 Acceleration
        {
            get { return acceleration; }

            set
            {
                acceleration = value;

                // Notify observers
                NotifyObservers();
            }
        }

        # endregion

        public override string ToString()
        {
            return "Body " + Name + " P{" + WorldPosition.ToString() + "} V{" + Velocity.ToString() + "} A{" + Acceleration.ToString() + "}";
        }
    }
}
